import React, { Component } from 'react'
import { FlatList, Text, View, StyleSheet, Keyboard } from 'react-native'
import PropTypes from 'prop-types'
import RefreshState from './RefreshState'
import RefreshFooter from './RefreshFooter'

export default class CustomFlatList extends Component {
  static propTypes = {
    onHeaderRefresh: PropTypes.func, // 下拉刷新的方法，供外部调用
    onFooterRefresh: PropTypes.func, // 上拉加载的方法，供外部调用
    data: PropTypes.array.isRequired, // 数据源
    renderItem: PropTypes.func.isRequired,
    // ListEmptyComponent: PropTypes.element, // 渲染空页面
    ListEmptyComponent: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), // 渲染空页面
    keyExtractor: PropTypes.func,
    style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
    extraData: PropTypes.object, // 需要触发刷新的数据
    pullDown: PropTypes.bool, // 是否开启下拉刷新
    pullUp: PropTypes.bool, // 是否开启上拉加载
    defaultLength: PropTypes.number, // 空页面的默认长度
    keyboardShouldPersistTaps: PropTypes.string,
    nestedScrollEnabled: PropTypes.bool,
    inverted: PropTypes.bool,
    showsVerticalScrollIndicator: PropTypes.bool,
    getItemLayout: PropTypes.func
  }

  static defaultProps = {
    extraData: {},
    pullDown: true,
    pullUp: true,
    defaultLength: 0,
    keyboardShouldPersistTaps: 'handled',
    nestedScrollEnabled: true,
    inverted: false
  }

  constructor(props) {
    super(props)
    this.state = {
      isHeaderRefreshing: false, // 头部是否正在刷新
      footerState: RefreshState.Idle, // 尾部当前的状态，默认为Idle，不显示控件
      fHeight: 0
    }
    this.isFooterRefreshing = false // 尾部是否正在刷新
  }

  render() {
    const {
      isHeaderRefreshing, footerState
    } = this.state
    const {
      data, renderItem, keyExtractor, style, extraData, pullDown, pullUp, ListEmptyComponent, keyboardShouldPersistTaps, nestedScrollEnabled, inverted, showsVerticalScrollIndicator, getItemLayout, ...rest
    } = this.props
    return (
      <FlatList
        extraData={{
          ...extraData, footerState
        }}
        inverted={inverted}
        ref={this._flatListRef}
        style={style}
        data={data}
        renderItem={renderItem}
        keyExtractor={keyExtractor}
        refreshing={isHeaderRefreshing}
        onRefresh={pullDown ? this.beginHeaderRefresh : null}
        onEndReached={pullUp ? this.beginFooterRefresh : null}
        onEndReachedThreshold={0.1} // 这里取值0.1（0~1之间不包括0和1），可以根据实际情况调整，取值尽量小
        ListFooterComponent={this._renderFooter}
        ListEmptyComponent={this._renderEmptyView}
        onLayout={this._onLayout}
        showsVerticalScrollIndicator={showsVerticalScrollIndicator}
        keyboardShouldPersistTaps={keyboardShouldPersistTaps}
        nestedScrollEnabled={nestedScrollEnabled}
        onScrollBeginDrag={this._onScrollBeginDrag}
        getItemLayout={getItemLayout}
        {...rest}
      />
    )
  }

  _onScrollBeginDrag = () => {
    Keyboard.dismiss()
  }

  _flatListRef = (ref) => {
    this.flatListRef = ref
  }

  scrollToTop = (animated, callback) => {
    this.callback = callback
    this.flatListRef.scrollToOffset({
      y: 0,
      animated
    })
  }

  _onLayout = (e) => {
    const height = e.nativeEvent.layout.height
    if (this.state.fHeight < height) {
      this.setState({
        fHeight: height
      })
    }
  }

  _renderEmptyView = () => {
    const {
      ListEmptyComponent
    } = this.props
    const {
      fHeight
    } = this.state
    return (
      <View style={{
        height: fHeight
      }}>
        {
          ListEmptyComponent ||
          (<View style={styles.emptyView}>
            <Text></Text>
          </View>)
        }
      </View>)
  }

  _renderFooter = () => {
    const {
      footerState
    } = this.state
    return (
      <RefreshFooter
        state={footerState}
        onRetryLoading={this.beginFooterRefresh}
      />
    )
  }

  /// 开始下拉刷新
  beginHeaderRefresh = () => {
    if (this.shouldStartHeaderRefreshing()) {
      this.startHeaderRefreshing()
    }
  }

  /**
   *  开始上拉加载更多
   */
  beginFooterRefresh = () => {
    if (this.shouldStartFooterRefreshing()) {
      this.startFooterRefreshing()
    }
  }

  /***
   * 当前是否可以进行下拉刷新
   * @returns {boolean}
   *
   * 如果列表尾部正在执行上拉加载，就返回false
   * 如果列表头部已经在刷新中了，就返回false
   */
  shouldStartHeaderRefreshing = () => {
    const {
      footerState,
      isHeaderRefreshing
    } = this.state
    return !(footerState === RefreshState.Refreshing || isHeaderRefreshing || this.isFooterRefreshing)
  }

  /***
   * 当前是否可以进行上拉加载更多
   * @returns {boolean}
   *
   * 如果底部已经在刷新，返回false
   * 如果底部状态是没有更多数据了，返回false
   * 如果头部在刷新，则返回false
   * 如果列表数据为空，则返回false（初始状态下列表是空的，这时候肯定不需要上拉加载更多，而应该执行下拉刷新）
   */
  shouldStartFooterRefreshing = () => {
    const {
      footerState,
      isHeaderRefreshing
    } = this.state
    const {
      data,
      defaultLength
    } = this.props
    return !(footerState === RefreshState.Refreshing ||
      footerState === RefreshState.NoMoreData ||
      data.length === defaultLength ||
      isHeaderRefreshing ||
      this.isFooterRefreshing)
  }

  /**
   * 下拉刷新，设置完刷新状态后再调用刷新方法，使页面上可以显示出加载中的UI，注意这里setState写法
   */
  startHeaderRefreshing = () => {
    const {
      onHeaderRefresh
    } = this.props
    this.setState(
      {
        footerState: RefreshState.Idle,
        isHeaderRefreshing: true
      }, () => {
        onHeaderRefresh && onHeaderRefresh()
      }
    )
  }

  /**
   * 上拉加载更多，将底部刷新状态改为正在刷新，然后调用刷新方法，页面上可以显示出加载中的UI，注意这里setState写法
   */
  startFooterRefreshing = () => {
    const {
      onFooterRefresh
    } = this.props
    this.isFooterRefreshing = true
    this.setState(
      {
        footerState: RefreshState.Refreshing
      }, () => {
        onFooterRefresh && onFooterRefresh()
      }
    )
  }

  /**
   * 根据尾部组件状态来停止刷新
   * @param footerState
   *
   * 如果刷新完成，当前列表数据源是空的，就不显示尾部组件了。
   * 这里这样做是因为通常列表无数据时，我们会显示一个空白页，如果再显示尾部组件如"没有更多数据了"就显得很多余
   */
  endRefreshing = (footerState: RefreshState) => {
    let footerRefreshState = footerState
    if (this.props.data.length === this.props.defaultLength) {
      footerRefreshState = RefreshState.Idle
    }
    this.isFooterRefreshing = false
    this.setState({
      footerState: footerRefreshState,
      isHeaderRefreshing: false
    })
  }
}

const styles = StyleSheet.create({
  emptyView: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    paddingLeft: 20,
    marginTop: 100
  }
})
